 <!-- dat.gui threejs 官方调试工具
  包围盒 boundingBox hreejs性能考虑，默认是不计算的，需要手动计算 computeBoundingBox   mesh.geometry.computeBoundingBox()
  视锥体 Frustum  用于确定相机视野内的东西。 它有助于加速渲染过程——位于摄像机视锥体外的物体可以安全地排除在渲染之外。
  该类主要用于渲染器内部计算 camera 或 shadowCamera的视锥体。
  -->
  <template>
    <div ref="Cavans"></div>
</template>
<script setup>
import { createHelper, resizeHandler, THREE, createGui } from '@/utils/threejs'
// import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { OrbitControls } from 'three/addons/controls/OrbitControls'
import Stats from 'three/examples/jsm/libs/stats.module';

const Cavans = ref(null)
const guiConfig = {
    wireframe: false,
    // 相机切换
    switchCamera: () => {
        if(currentCamera === watcherCamera){
            currentCamera = camera
            controls.enabled = false
        } else {
            currentCamera = watcherCamera
            controls.enabled = true
        }
    }
}
let currentCamera
// 创建一个场景
const scene = new THREE.Scene()
// 初始化调试
const gui = createGui()
// 创建一个透视投影相机相机  PerspectiveCamera 
const camera = new THREE.PerspectiveCamera( 75, document.body.clientWidth / document.body.clientHeight, 1, 20 )
camera.lookAt(scene.position)
scene.add(camera)
// 相机位置
camera.position.set(0, 0, 5);// 相机默认坐标是(0,0,0)
// 添加第二个相机   PerspectiveCamera 透视投影相机 四个参数 视野角度（FOV）长宽比（aspect ratio）近截面（near） 远截面（far）
const watcherCamera = new THREE.PerspectiveCamera( 75, document.body.clientWidth / document.body.clientHeight, 0.1, 100 )
watcherCamera.position.set(1, 0.5, 10)
watcherCamera.lookAt(scene.position)
scene.add(watcherCamera)
currentCamera = watcherCamera
// 添加相机辅助
const cameraHelper = new THREE.CameraHelper(camera)
scene.add(cameraHelper)
// 渲染器
const renderer = new THREE.WebGLRenderer({
    antialias: true, // 抗锯齿
})
// 创建辅助
createHelper(scene)
// 优化 设置像素比
renderer.setPixelRatio(window.devicePixelRatio || 1)
// 渲染器的尺寸
renderer.setSize(document.body.clientWidth, document.body.clientHeight)
// geometry 几何体 创建一个立方体 BoxGeometry
const geometry = new THREE.BoxGeometry( 1.5, 1.5, 1.5 )
// 添加全局光照 AmbientLight ambientlight( color : Integer, intensity : Float ) 环境光会均匀的照亮场景中的所有物体。
// color - (参数可选）颜色的rgb数值。缺省值为 0xffffff。 十六进制
// intensity - (参数可选)光照的强度。缺省值为 1。
const light = new THREE.AmbientLight(0xffffff, 0.5)
// 添加方向光 平行光（directionallight） 平行光是沿着特定方向发射的光
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
scene.add(light, directionalLight)
// 颜色
const color = new THREE.Color(Math.random(), Math.random(), Math.random())
const faces = []
for(let i = 0; i < geometry.groups.length; i++){
    const material = new THREE.MeshBasicMaterial({
        color: new THREE.Color(Math.random(), Math.random(), Math.random()),
    })
    faces.push(material)
}
// Mesh 网格  网格包含一个几何体 geometry 以及作用在此几何体上的材质 material
const cube = new THREE.Mesh( geometry, faces );
// 调用 scene.add()，物体将会被添加到 (0,0,0) 坐标
scene.add(cube)
const controls = new OrbitControls(currentCamera,renderer.domElement) 
// 将其设置为true以启用阻尼（惯性），这将给控制器带来重量感。默认值为false。 请注意，如果该值被启用，你将必须在你的动画循环里调用update()
controls.enableDamping = true 
// 当.enableDamping设置为true的时候，阻尼惯性有多大。 Default is 0.05. 请注意，要使得这一值生效，你必须在你的动画循环里调用 update()
controls.dampingFactor = 0.03
// 添加性能监控器
const stats = new Stats()
stats.setMode(0)
const clock = new THREE.Clock()
const animate = () => {
    const elapsedTime = clock.getElapsedTime()
    // 浏览器API
	requestAnimationFrame( animate );
    // 相机运动
    cube.position.x = Math.cos(elapsedTime)
    cube.position.y = Math.sin(elapsedTime)
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01; 
    controls.update() // 更新轨道控制器  
    stats.update() // 更新监控帧数
    cameraHelper.update() 
	renderer.render( scene, currentCamera );
}
animate();
// 自适应
resizeHandler(renderer, currentCamera)
// 封装方法 判断是否相交
const isIntersect = () => {
    // 通过camera 计算视锥
    const frustum = new THREE.Frustum()
    camera.updateProjectionMatrix()
    // setFromProjectionMatrix 根据投影矩阵 matrix 来设置当前视椎体的六个面
    // Matrix4 四维矩阵
    // .multiplyMatrices ( a : Matrix4, b : Matrix4 ) : this
    // 设置当前矩阵为矩阵a x 矩阵b。
    // matrixWorldInverse 逆矩阵
    // projectionMatrix 投影矩阵
    // 相乘得到视锥体的矩阵 返回 true 如果该 box 与视锥体相交
    frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse))
    // boundingBox 包围盒 threejs性能考虑，默认是不计算的，需要手动计算 computeBoundingBox
    cube.geometry.computeBoundingBox()
    // intersectsBox 用于检测是否要交的包围盒。
    const result = frustum.intersectsBox(cube.geometry.boundingBox)
    console.log('=====result', result)
}
isIntersect()
// gui 调试
// gui.add() 
// 参数 target: object, propName: never, min?: number, max?: number, step?: number
gui.add(currentCamera.position,'x',0.1,10,0.1).name('positionX')
gui.add(cube.rotation,'x',1,10,1).name('rotationX')
gui.add(camera,'far',0.1,30,0.1).onChange((val)=>{
    camera.far = val
    camera.updateProjectionMatrix()
    isIntersect()
})
gui.add(camera,'near',0.1,30,0.1).onChange((val)=>{
    camera.near = val
    camera.updateProjectionMatrix()
    isIntersect()
})
// 参数为自定义对象
gui.add(guiConfig,'wireframe').onChange((val)=>{
    // wireframe 是否启用线框模式，默认为 false
    cube.material.forEach((item)=>item.wireframe=val)
})
// 参数为函数
gui.add(guiConfig,'switchCamera')
onMounted(()=>{
    // 挂载
    Cavans.value.appendChild(renderer.domElement)
    Cavans.value.appendChild(stats.domElement)
})
</script>
